
//
//  QGStatusListViewModel.swift
//  斯威夫特微博
//
//  Created by youyoumacmini3 on 2017/7/10.
//  Copyright © 2017年 youyoumacmini3. All rights reserved.
//

import Foundation
import SDWebImage

/// 微博数据列表视图模型
/// 父类的选择
/*
 1. 如果类需要使用'KVC' 或者字典转模型框架设置对象值，类就需要继承自NSObject
 2. 如果类知识包装代码逻辑（写了一些函数），可以不用任何父类。好处：更加轻量级
 -. 提示：如果用OC 写，一律都继承自 NSObject 即可
 
 使命： 负责微博数据处理
 1. 字典转模型
 2. 下拉/上拉刷新数据处理
 */

// 上拉加载最大尝试此数
private let maxPullTryTime = 3

class QGStatusListViewModel {
    
    /// 微博视图模型数组懒加载
    lazy var statusList = [QGStatusViewModel]()
    
    
    private var pullUpErrorTimes = 0
    
    /// 加载微博列表
    ///
    /// - Parameter pullUp: 是否上拉加载标记
    /// - Parameter completion: 完成回调[网络请求是否成功,是否可以继续上拉加载刷新表格]
    func loadStatus(pullUp: Bool, completion:@escaping(_ isSuceess:Bool, _ shouldRefresh: Bool)->()) {
        
        // 判断是否是上拉加载，同时检查刷新错误
        if pullUp && pullUpErrorTimes > maxPullTryTime {
            completion(true, false)
            return
        }
        
        
        // since_id 取出数组中第一条微博的 ID (处理下拉加载新数据)
        let since_id = pullUp ? 0 : (statusList.first?.status.id ?? 0)
        // 上拉加载，取出微博最后一条微博的id
        let max_id = !pullUp ? 0 : (statusList.last?.status.id ?? 0)
        
    
        // 让数据访问层加载数据
        QGStatusListDAL.loadStatus(since_id: since_id, max_id: max_id) { (list, isSuccess) in
      
            /*
        }
        // 发起网络请求 加载微博数据（字典数组）- 此处改成从本地数据库加载数据了，如果本地没有该数据，在从网络请求数据
        QGNetworkManager.shared.statusList(since_id: since_id, max_id: max_id) { (list, isSuccess) in
      */
            
            // 0. 如果网络请求失败，直接执行完成回调
            if !isSuccess {
                completion(false, false)
                return
            }
            
            // 1. 遍历字典数组 => 字典转模型 -> 视图模型，将视图模型添加到数组
            var array = [QGStatusViewModel]()
            
            for dic in list ?? [] {
                // 1> 创建微博模型
                let status = QGStatus()
                
                // 2> 使用字典设置模型数值
                status.yy_modelSet(with: dic)
                
                // 3> 使用 '微博' 模型创建 '微博视图' 模型
                let viewModel = QGStatusViewModel(model: status)
                
                // 4> 添加到数组
                array.append(viewModel)
            }
            
            // 视图模型创建完成
            
            print("刷新到 \(array.count) 条数据\(array)")
            // 2. FIXME: 拼接数据
            if pullUp {
                // 上拉加载，将结果拼接在数组的末尾
                self.statusList += array
            }else{
                // 下拉刷新，应该将结果拼接在数组前面
                self.statusList = array + self.statusList
            }
            
            // 3.判断上拉加载的数据量
            // 当上拉状态并且 新增数据为0的情况下 增加上拉错误数
            if pullUp && array.count == 0 {
                self.pullUpErrorTimes += 1
                completion(isSuccess, false)//(因为数据为0了，所以传false,因为也没必要刷新了 )
            }else{
                
                self.cacheSingleImage(list: array, finished: completion)
                
                // 4.完成回调(真正有数据的回调)
                // 不要了 ，传到下面单张图片问题搞定之后在回调去了
                //completion(isSuccess, true)
            }
            
            
            
//            // 0.网络请求是否成功
//            if !isSuccess {
//                //直接回调返回
//                completion(false, false)
//            }
//            
//            
//            // 1.字典转模型
//            // 1>> 定义结果可变数组
//            var array = [QGStatusViewModel]()
//            
//            // 2>> 遍历服务器返回的字典数组，字典转模型
//            for dict in list ?? [] {
//                // a. 创建微博模型 - 如果创建模型失败，继续后续的遍历(所以不能用 return )
//                guard let model = QGStatus.yy_model(with: dict) else {
//                    continue
//                }
//                
//                // b. 将 视图模型 添加到数组
//                array.append(QGStatusViewModel(model: model))
//            }

//            
//            guard let array = NSArray.yy_modelArray(with: QGStatus.self, json: list ?? []) as? [QGStatus] else{
//                //字典转模型失败，没有数据时进来
//                completion(isSuccess,false)
//                return
//            }
//            
//
//
        }
    }
    
    
    /// 缓存本次下载微博数组中的单张图像
    ///  - 应该缓存完单张图像，并且修改过配图视图的大小之后，在回调，才能保证表格等比例显示单张图像
    ///
    /// - Parameter list: 本次下载的视图模型数组
    private func cacheSingleImage(list: [QGStatusViewModel], finished:@escaping(_ isSuceess:Bool, _ shouldRefresh: Bool)->()) {
        
        
        // 调度组
        let group = DispatchGroup()
        
        
        
        // 记录数据长度
        var length = 0
        
        // 遍历数组，查找数组中有单张图像的，进行缓存
        for vm in list {
            
            // 1. 判断图像数量
            if vm.picURLs?.count != 1 {
                continue
            }
            // 2. 获取图像模型(代码执行到此，数组中有且仅有一张图片)
            guard let pic = vm.picURLs?[0].thumbnail_pic,
                  let url = URL(string: pic) else {
                    continue
            }
            
            // 3. 下载图像
            // 1）loadImage 是 SDWebImage 核心方法
            // 2）图像下载完成后，会自动保存在沙盒中，文件路径是 url 的MD5
            // 3）如果沙盒中已经存在缓存的图像，后续使用 SDWebImage 加载图像，都会在家本地沙盒的图像
            // 4）不会发起网络请求，同时，回调方法同样会调用
            // 5）方法还是同样的方法，调用还是同样的调用，不过内部不会再次发起网络请求
            // ** 注意： 如果缓存的图像累计很大，要找后台要接口
            
            // a.入组
            group.enter()
            SDWebImageManager.shared().loadImage(with: url, options: [], progress: nil, completed: { (image, _, _, _, _, _) in
                
                // 将图像转成二进制数据
                if let image = image,
                    let data = UIImagePNGRepresentation(image) {
                    // NSData 是 length 属性
                    length += data.count
                    
                    // 图像缓存成功，更新配图视图的大小
                    vm.updateSingleImageSize(image: image)
                    
                }
                print("缓存下来的图片是：\(image) 长度：\(length)")
                // b. 出组 - 放在毁掉最后一句
                group.leave()
            })
        }
        
        // c. 监听调度组情况
        group.notify(queue: DispatchQueue.main) { 
            print("图像缓存成功 \(length / 1024) k")
            
            // 执行闭包回调
            finished(true, true)
        }
    }
}
